use chrono::prelude::*;
use rbatis::core::db::DBExecResult;
use rbatis::crud::{Skip, CRUD};
use rbatis::rbatis::Rbatis;

use crate::features::spreadsheet::modelsdbo::CellEdgeDBO;

/// find edge by source,predicate,object
pub async fn find_cell_edge(
  rbatis_pool: &Rbatis,
  subject: &str,
  predicate: &str,
  object: &str,
) -> Result<CellEdgeDBO, rbatis::Error> {
  let spo_wrapper = rbatis_pool
    .new_wrapper()
    .eq("subject", subject)
    .and()
    .eq("predicate", predicate)
    .and()
    .eq("object", object);

  rbatis_pool.fetch_by_wrapper::<CellEdgeDBO>(spo_wrapper).await
}

pub async fn upsert_cell_edgedbo(rbatis_pool: &Rbatis, edge: &CellEdgeDBO) -> Result<CellEdgeDBO, rbatis::Error> {
  upsert_cell_edge(
    rbatis_pool,
    &edge.subject,
    &edge.predicate,
    &edge.object,
    &edge.properties,
  )
  .await
}

/// (user_abc) is (tester) of (quiz_xyz)
/// upsert edge by source,predicate,object
pub async fn upsert_edge(
  rbatis_pool: &Rbatis,
  subject: &str,
  predicate: &str,
  object: &str,
  properties: &str,
) -> Result<CellEdgeDBO, rbatis::Error> {
  let edge_dbo = self::find_cell_edge(rbatis_pool, subject, predicate, object).await;
  if edge_dbo.is_ok() {
    let updated = update_cell_edge(rbatis_pool, subject, predicate, object, properties).await;
  } else {
    let inserted = insert_cell_edge(rbatis_pool, subject, predicate, object, properties).await;
  }

  self::find_cell_edge(rbatis_pool, subject, predicate, object).await
}

/// insert edge by source,predicate,object
pub async fn insert_cell_edge(
  rbatis_pool: &Rbatis,
  subject: &str,
  predicate: &str,
  object: &str,
  properties: &str,
) -> Result<DBExecResult, rbatis::Error> {
  let now: DateTime<Utc> = Utc::now(); // e.g. `2014-11-28T12:45:59.324310806Z`

  let edge_dbo = CellEdgeDBO {
    subject: subject.to_owned(),
    predicate: predicate.to_owned(),
    object: object.to_owned(),
    properties: properties.to_owned(),
    created_at: Some(now.timestamp()),
    updated_at: Some(now.timestamp()),
  };

  let db_result = rbatis_pool
    .save::<EdgeDBO>(
      &edge_dbo,
      &[Skip::Value(bson2::Bson::Null), Skip::Column("id")],
    )
    .await;

  db_result
}

/// update edge by source,predicate,object
pub async fn update_cell_edge(
  rbatis_pool: &Rbatis,
  subject: &str,
  predicate: &str,
  object: &str,
  properties: &str,
) -> Result<u64, rbatis::Error> {
  let now: DateTime<Utc> = Utc::now(); // e.g. `2014-11-28T12:45:59.324310806Z`
  let id_wrapper = rbatis_pool
    .new_wrapper()
    .eq("subject", subject)
    .and()
    .eq("predicate", predicate)
    .and()
    .eq("object", object);

  let edge_dbo = CellEdgeDBO {
    subject: subject.to_owned(),
    predicate: predicate.to_owned(),
    object: object.to_owned(),
    properties: properties.to_owned(),
    updated_at: Some(now.timestamp()),
    ..Default::default()
  };

  let updated = rbatis_pool
    .update_by_wrapper(
      &edge_dbo,
      id_wrapper,
      &[
        Skip::Value(bson2::Bson::Null),
        Skip::Column("subject"),
        Skip::Column("predicate"),
        Skip::Column("object"),
      ],
    )
    .await;

  updated
}

/// delete edge by source,predicate,object
pub async fn delete_cell_edge(
  rbatis_pool: &Rbatis,
  subject: &str,
  predicate: &str,
  object: &str,
) -> Result<u64, rbatis::Error> {
  let id_wrapper = rbatis_pool
    .new_wrapper()
    .eq("subject", subject)
    .and()
    .eq("predicate", predicate)
    .and()
    .eq("object", object);

  let db_result = rbatis_pool.remove_by_wrapper::<CellEdgeDBO>(id_wrapper).await;

  db_result
}
